package com.google.code.gwtquickstartplugin;

import org.apache.maven.plugin.logging.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;

import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;

/**
 * Small class with utilities for XML and file processing.
 *
 * @author Carlos D. Morales
 */
public class XmlUtils {

    public static Element createElementWithText(Document doc, String elementName, String contents){
        Element element = doc.createElement(elementName);
        element.appendChild(doc.createTextNode(contents));
        return element;
    }

    public static void verifyFileExists(Log log, String xmlFilePath) {
        if (xmlFilePath == null) {
            throw new RuntimeException(
                    "File name is null. " +
                            "Please configure the gwtquickstart-plugin gwtModule property " +
                            "and ensure web_.xml is under src/main/webapp/WEB-INF.");
        }
        log.debug("Parsing file [" + xmlFilePath + "]");
        if (!(new File(xmlFilePath)).exists()) {
            throw new RuntimeException(
                    "File [" + xmlFilePath + "] could not be found");
        }
    }

    public static Document parseXmlFileToDom(Log log, String xmlFilePath) {
        Throwable exception = null;
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        Document doc = null;
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            doc = db.parse(xmlFilePath);
        } catch (ParserConfigurationException pce) {
            log.warn(pce);
            exception = pce;
        } catch (SAXException se) {
            log.warn(se);
            exception = se;
        } catch (IOException ioe) {
            log.warn(ioe);
            exception = ioe;
        }
        if (exception != null) {
            throw new RuntimeException(
                    "An error ocurred while parsing the XML file [" +
                            xmlFilePath + "].");
        }
        return doc;
    }

    public static void addElements(Log log, final Node parent,
                                   Element[] children,
                                   boolean removeAllElements) {
        if (removeAllElements) {
            removeAll(parent, Node.ELEMENT_NODE, "servlet");
            removeAll(parent, Node.ELEMENT_NODE, "servlet-mapping");
        }

        for (int i = 0; i < children.length; i++) {
            parent.appendChild(children[i]);
        }
    }

    public static void removeAll(Node node, short nodeType, String name) {
        if (node.getNodeType() == nodeType &&
                (name == null || node.getNodeName().equals(name))) {
            node.getParentNode().removeChild(node);
        } else {
            // Visit the children
            NodeList list = node.getChildNodes();
            for (int i = 0; i < list.getLength(); i++) {
                removeAll(list.item(i), nodeType, name);
            }
        }
    }

    public static void removeChildElements(Log log, Document docRoot,
                                           String tagName) {
        NodeList servletNodes = docRoot.getElementsByTagName(tagName);
        if (servletNodes != null && servletNodes.getLength() > 0) {
            log.debug("Removing " + servletNodes.getLength() +
                    " elements");
            for (int i = 0; i < servletNodes.getLength(); i++) {
                Element servletNode = (Element) servletNodes.item(i);
                docRoot.removeChild(servletNode);
            }
        }
    }

    public static String getTextValue(Element element, String childTagName) {
        String textVal = null;
        NodeList nl = element.getElementsByTagName(childTagName);
        if (nl != null && nl.getLength() > 0) {
            Element el = (Element) nl.item(0);
            textVal = el.getFirstChild().getNodeValue();
        }
        return textVal;
    }

    public static Object[] parseForElements(
            Log log,
            Document docRoot, String tagName, Object[] emptyArray,
            ElementMapper mapper) {
        NodeList servletNodes = docRoot.getElementsByTagName(tagName);
        Object[] result =
                emptyArray;
        if (servletNodes != null && servletNodes.getLength() > 0) {
            log.debug(servletNodes.getLength() +
                    " <" + tagName + "> elements found in GWT Module file");
            result = mapper.buildArrayForResults(servletNodes.getLength());
            for (int i = 0; i < servletNodes.getLength(); i++) {
                Element servletNode = (Element) servletNodes.item(i);
                result[i] = mapper.mapElement(log, servletNode);
            }
        }
        return result;
    }

    public static void serializeDocument(String file, Document dom) {
        try {
            OutputFormat format = new OutputFormat(dom);
            format.setIndenting(true);

            //to generate output to console use this serializer
            //XMLSerializer serializer = new XMLSerializer(System.out, format);

            //to generate a file output use fileoutputstream instead of system.out
            XMLSerializer serializer = new XMLSerializer(
                    new FileOutputStream(new File(file)), format);

            serializer.serialize(dom);

        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }

}
